/*
 * @file chip_sct.h
 * @author suyong_yq@126.com
 *
 * @brief LPC8xx SCT driver
 * @note
 * 基于LPCOpen中sct_8xx及sct_pwm_8xx驱动程序改写
 */

#ifndef __SCT_8XX_H_
#define __SCT_8XX_H_

#include "chip.h"

/* 定义定时器的标识符 */
#define SCT_COUNTER_ID_Low16bit_MASK   (0x1 << 0)
#define SCT_COUNTER_ID_High16bit_MASK  (0x1 << 1)
#define SCT_COUNTER_ID_Unify32bit_MASK SCT_COUNTER_ID_Low16bit_MASK
#define SCT_COUNTER_ID_NONE_MASK       (0)

/*!
 * @brief Selection of SCT counter mode.
 */
typedef enum
{
    eSCT_CounterMode_32bitx1 = 1U, /*!< 单32bit计数器模式. */
    eSCT_CounterMode_16bitx2 = 0U, /*!< Two 16 bit counter mode. */
} SCT_CounterMode_T;

/*!
 * @brief Selection of SCT counter's clock source.
 */
typedef enum
{
	/* 整个SCT模块包括计数过程都使用System Clock或者某些描述称之为Bus Clock. */
	eSCT_ClockSource_SystemClock = 0U,

	/* SCT模块的工作逻辑由System Clock驱动，但计数时钟及分频器时钟则由SCT_CONFIG_CKSEL指定，
	 * 此时最小的采样间隔为一个System Clock的时钟 */
	eSCT_ClockSource_SampledSystemClock = 1U,

    /* SCT Input Clock Mode. The input/edge selected by the CKSEL field clocks the 
     * SCT module, including the counters and prescalers, after first being 
     * synchronized to the system clock. The minimum pulse width on the clock input 
     * is 1 bus clock one system clock period. This mode is the low-power, 
     * sampled-clock mode.
     * 此时SCT Clock时钟仍为System Clock。
     */
	eSCT_ClockSource_InputClock = 2U,

	/* Asynchronous Mode. The entire SCT  module is clocked directly by the 
     * input/edge selected by the CKSEL field. In this  mode, the SCT outputs are 
     * switched synchronously to the SCT input clock - not the system clock. The input 
     * clock rate must be at least half the system clock rate and can be the same or 
     * faster than the system clock.
     * SCT模块完全独立于System Clock工作，要求输入时钟至少为System Clock的一半，
     * 频率可以比System Clock高
	 */
	 eSCT_ClockSource_AsynchronousClock = 3U,
} SCT_ClockSource_T;

/*!
 * SCT clock select. The  specific functionality of the designated input/edge is 
 * dependent on the CLKMODE bit selection in this register.
 */
typedef enum
{
    eSCT_ClcokEdge_RisingEdgeOnInput0  = 0U,
    eSCT_ClcokEdge_FallingEdgeOnInput0 = 1U,
    eSCT_ClcokEdge_RisingEdgeOnInput1  = 2U,
    eSCT_ClcokEdge_FallingEdgeOnInput1 = 3U,
    eSCT_ClcokEdge_RisingEdgeOnInput2  = 4U,
    eSCT_ClcokEdge_FallingEdgeOnInput2 = 5U,
    eSCT_ClcokEdge_RisingEdgeOnInput3  = 6U,
    eSCT_ClcokEdge_FallingEdgeOnInput3 = 7U,
} SCT_ClockEdge_T;


/*!
 * @brief Configuration of SCT counter.
 */
typedef struct
{
	SCT_CounterMode_T CounterMode; /*!< Counter mode. */
    SCT_ClockSource_T ClockSource; /*!< 时钟源模式. */
    SCT_ClockEdge_T   ClockEdge;   /*!< 采样边沿配置，依赖于时钟源的设定. */

    /* 若是为某个定时器启用NoReload功能，在匹配计数时，对应定时器的Match寄存器将会复用最近一次的写入
     * 值，而不是从Match Reload Register中装载。 */
    uint32_t EnableNoReloadCounterMaskValue; /* SCT_COUNTER_ID_x_MASK */

    /*
    * 某个bit为1对应SCT input与SCT工作时钟同步。如果input的时钟已经确认同步于SCT clock(在eSCT_ClockSource_AsynchronousClock
    * 模式下，Input与工作时钟均为Input时钟；在eSCT_ClockSource_SystemClock模式下，Input与工作时钟均为System Clock，
    * 这两种情况下Input时钟和SCT工作时钟同步)，当然也可以直接关闭人为的同步功能，以提高响应速率。
    * Note that the INSYNC field only affects inputs used for event generation. It does 
    * not apply to the clock input specified in the CKSEL field.
    */
    uint32_t SyncInputMaskValue; /* 5-bit available，每bit对一个Input通道。 */

    /* 启用循环计数模式，使用SCT_MATCH[0]中的设定值作为循环周期，而不需要在专门设定事件寄存器。
     * 在双向计数模式下，自动循环将改变计数方向。
     * 若不启用循环计数模式，在默认情况下对应Free Run模式。 */
    uint32_t EnableAutoLimitCounterMaskValue; /* SCT_COUNTER_ID_x_MASK */
} SCT_CounterConfig_T;

/*!
 * @brief 定义SCT Counter控制命令
 */
typedef enum
{
    eSCT_CounterCtrlCmd_DOWN   = SCT_CTRL_DOWN_L_MASK,   /*!< Force to direction to count down. */
    eSCT_CounterCtrlCmd_STOP   = SCT_CTRL_STOP_L_MASK,
    eSCT_CounterCtrlCmd_HALT   = SCT_CTRL_HALT_L_MASK,
    eSCT_CounterCtrlCmd_CLRCTR = SCT_CTRL_CLRCTR_L_MASK, /*!< 清零计数器. */
    eSCT_CounterCtrlCmd_BIDIR  = SCT_CTRL_BIDIR_L_MASK,  /*!< 启用双向计数模式 */
    eSCT_CounterCtrlCmd_PRE    = SCT_CTRL_PRE_L_MASK,    /*!< 设定计数器的分频因子 */
} SCT_CounterCtrlCmd_T;

/*!
 * @brief 选择SCT Event对Match/Capture Register的使用方式
 *
 * SCT的Match/Capture Register复用的同一个寄存器地址，在各自不同的SCT工作模式下，工作在不同的功能
 * 切换功能时，需要设定REGMODEn寄存器位
 */
typedef enum
{
    eSCT_EventCounterMode_Match   = 0U, /*!< Match/Capture register works as Match Register. */
    eSCT_EventCounterMode_Capture = 1U, /*!< Match/Capture register works as Capture Register. */
} SCT_EventCounterMode_T;

/*!
 * @brief 选择SCT Event对IO信号的使用方式
 */
typedef enum
{
	eSCT_EventIOMode_Input = 0U, /*!< 使用Input触发Event */
	eSCT_EventIOMode_Output = 1U, /*!< Event驱动Output触发其它模块 */
} SCT_EventIOMode_T;

/*!
 * @brief
 *
 * Selects the I/O condition for event n. (The detection of edges on outputs lag the
 * conditions that switch the outputs by one SCT clock). In order to guarantee proper
 * edge/state detection, an input must have a minimum pulse width of at least one SCT
 * clock period .
 */
typedef enum
{
	eSCT_EventIOCondition_LowLevel = 0U, /*!<  */
    eSCT_EventIOCondition_RisingEdge = 1U,
    eSCT_EventIOCondition_FallingEdge = 2U,
    eSCT_EventIOCondition_HighLevel = 3U,
} SCT_EventIOCondition_T;

/*!
 * @brief 选择事件触发条件的组合模式
 */
typedef enum
{
    eSCT_EventCondition_MatchOrIO = 0U,
    eSCT_EventCondition_MatchOnly = 1U,
    eSCT_EventCondition_IOOnly = 2U,
    eSCT_EventCondition_MatchAndIO = 3U,
} SCT_EventCondition_T;

/*!
 * @brief 选择本事件被触发后，对STATE值的更新策略
 */
typedef enum
{
    eSCT_EventStateUpdate_AddWithNewValue = 0U,
    eSCT_EventStateUpdate_LoadWithNewValue = 1U,
} SCT_EventStateUpdate_T;

/*!
 * @brief 选择本事件触发条件对计数器方向的限制
 *
 * 本配置项仅在双向计数模式下有效
 */
typedef enum
{
    eSCT_EventMatchDirection_NoCare = 0U,
    eSCT_EventMatchDirection_OnCountUp = 1U, /* 在双向计数模式下，只匹配递增模式下的Match值 */
    eSCT_EventMatchDirection_OnCountDown = 2U, /* 在双向计数模式下，只匹配递减模式下的Match值 */
} SCT_EventMatchDirection_T;

/*!
* @brief 配置Event触发条件
*/
typedef struct
{
    uint32_t                  MatchRegIdx; /*!< 指定哪个Match Register触发本Event，0-7 */
    uint32_t                  CounterId; /*!< 使用L计时器还是H计时器对应的状态机，SCT_COUNTER_ID_x_MASK */
    SCT_EventIOMode_T         EventIOMode; /*!< 接收Input触发开始响应事件，或是响应事件后Output触发其它模块 */
    uint32_t                  EventIOIdx; /*!< 与EventIOMode的设定相关，Input和Output各有一套引脚编号数组 */
    SCT_EventIOCondition_T    EventIOCondition; /*!< 接受触发/输出信号在引脚上的电平状态 */
    SCT_EventCondition_T      EventCondition; /*!< 事件被触发的混合模式 */
    SCT_EventStateUpdate_T    EventStateUpdate; /*!< STATE值的更新策略 */
    uint32_t                  EventUpdateNewValue; /*!< STATE更新值，5-bit有效 */
    bool                      EnableAdditionalCompareRange; /*!< 在匹配模式下，递增计数是否包含大于匹配值的范围，递减计数是否包含小于匹配值的范围 */
    SCT_EventMatchDirection_T EventMatchDirection; /*!< 在双向计数模式下，触发条件对计数方向的限制 */
    uint32_t                  EnableInStatesMaskValue; /*!< 本事件可以在哪些State下接收触发信号 */
} SCT_EventConditionConfig_T;

/*!
* @brief 定义当同一个Output信号被多个Event控制发生冲突时的解决方案
*/
typedef enum
{
    eSCT_OutputConflictResolution_NoChange = 0U,
    eSCT_OutputConflictResolution_Set = 1U,
    eSCT_OutputConflictResolution_Clear = 2U,
    eSCT_OutputConflictResolution_Toggle = 3U,
} SCT_OutputConflictResolution_T;

/*!
* @brief 定义在双向计数模式下，递减计数时输出信号的操作逻辑是否反转
*
* 正常情况下，匹配事件发生后，无论计数方向如何，输出信号就按照预先设定的拉高或者拉低。
* 但在使用了此处的配置后，在L或H计数器递减阶段，可以在原来本该拉低的操作转换为拉高操作，
* 这样的设计是为了便于输出对称波形：例如，计数值较小时输出低电平，递增达到Match值输出高电平，
* 碰到计数上限后回转递减，再次达到Match值反转输出高电平的逻辑，转而输出低电平。
* 在一个双向计数周期中，刚好产生一个对称的PWM波形。
*/
typedef enum
{
    eSCT_OutputModeInBiDirCounting_Normal = 0U, /* Set and clear do not depend on the direction of any counter. */
    eSCT_OutputModeInBiDirCounting_RevertLogicForCounterLDown = 1U, /*!< Set and clear are reversed when counter L or the unified counter is counting down. */
    eSCT_OutputModeInBiDirCounting_RevertLogicForCounterHDown = 2U, /*!< Set and clear are reversed when counter H is counting down. */
} SCT_OutputModeInBiDirCounting_T;

#ifdef __cplusplus
extern "C" {
#endif

/* Basic Counter. */
void Chip_SCT_ConfigCounter(LPC_SCT_T *base, const SCT_CounterConfig_T *config);
void Chip_SCT_ControlCounter(LPC_SCT_T *base, uint32_t counterIdMask, SCT_CounterCtrlCmd_T cmd, uint32_t param);
uint32_t Chip_SCT_GetCurrentCounterValue(LPC_SCT_T *base, uint32_t counterId);

/* Event Control. */
void Chip_SCT_EnableEvent(LPC_SCT_T *base, uint32_t eventMask, bool enable);
void Chip_SCT_EnableEventInterrupt(LPC_SCT_T *base, uint32_t eventMask, bool enable);
uint32_t Chip_SCT_GetEventFlag(LPC_SCT_T *base);
void Chip_SCT_ClearEventFlag(LPC_SCT_T *base, uint32_t eventMask);

/* Event Action. */
void Chip_SCT_PreSetEventLimitCounterCmd(LPC_SCT_T *base, uint32_t counterIdMask, uint16_t eventMask, bool enable);
void Chip_SCT_PreSetEventHaltCounterCmd(LPC_SCT_T *base, uint32_t counterIdMask, uint16_t eventMask, bool enable);
void Chip_SCT_PreSetEventStopCounterCmd(LPC_SCT_T *base, uint32_t counterIdMask, uint16_t eventMask, bool enable);
void Chip_SCT_PreSetEventStartCounterCmd(LPC_SCT_T *base, uint32_t counterIdMask, uint16_t eventMask, bool enable);
/* Pin. */
void Chip_SCT_PreSetEventSetOutputCmd(LPC_SCT_T *base, uint32_t outputId, uint16_t eventMask, bool enable);
void Chip_SCT_PreSetEventClearOutputCmd(LPC_SCT_T *base, uint32_t outputId, uint16_t eventMask, bool enable);
void Chip_SCT_SetOutputConflictSolution(LPC_SCT_T *base, uint32_t outputId, SCT_OutputConflictResolution_T resolution);
void Chip_SCT_SetOutputModeInBiDirCounting(LPC_SCT_T *base, uint32_t outputId, SCT_OutputModeInBiDirCounting_T mode);

/* Event Match/Capture Register. */
void Chip_SCT_SetEventModes(LPC_SCT_T *base, uint32_t counterIdMask, uint16_t matchMask, SCT_EventCounterMode_T mode);
void Chip_SCT_SetEventMatchValue(LPC_SCT_T *base, uint32_t counterIdMask, uint32_t matchRegIdx, uint32_t value);
void Chip_SCT_SetEventMatchReloadValue(LPC_SCT_T *base, uint32_t counterIdMask, uint32_t matchRegIdx, uint32_t value);
uint32_t Chip_SCT_GetEventCaptureValue(LPC_SCT_T *base, uint32_t counterId, uint32_t captureIdx);
/* Event Condition. */
void Chip_SCT_ConfigEventCondition(LPC_SCT_T *base, uint32_t eventIdx, const SCT_EventConditionConfig_T *config);

/* State Machine. */
void Chip_SCT_SetCurrentStateVariable(LPC_SCT_T *base, uint32_t counterIdMask, uint16_t curStateValue);
uint32_t Chip_SCT_GetCurrentStateVariable(LPC_SCT_T *base, uint32_t counterId);

#ifdef __cplusplus
}
#endif

#endif /* __SCT_8XX_H_ */
